package mongotesting

import (
	"context"
	"fmt"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"testing"
	"time"
)

const (
	image         = "mongo:latest"
	containerName = "car_rental_test_mongo"
	containerPort = "27017/tcp"
)

const defaultMongoURI = "mongodb://localhost:57017"

var mongoURL string

// RunWithMongoInDocker 使用docker容器中的mongodb实例运行测试。
func RunWithMongoInDocker(m *testing.M, runtime time.Duration) int {

	c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())

	if err != nil {
		panic(err)
	}

	ctx := context.Background()

	// 创建容器
	resp, err := c.ContainerCreate(ctx,
		&container.Config{
			Image:        image,
			ExposedPorts: nat.PortSet{containerPort: {}},
		},
		&container.HostConfig{PortBindings: nat.PortMap{
			containerPort: []nat.PortBinding{{HostIP: "127.0.0.1", HostPort: "57017"}}},
		},
		nil,
		nil,
		containerName)

	if err != nil {
		panic(err)
	}

	// 在函数退出时再Remove
	defer func(runtime time.Duration) {

		time.Sleep(runtime)

		err = c.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{
			Force:         true,
			RemoveVolumes: true,
		})

		if err != nil {
			panic(err)
		}

	}(runtime)

	err = c.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})

	if err != nil {
		panic(err)
	}

	// 检查容器
	inspectRes, err := c.ContainerInspect(ctx, resp.ID)

	if err != nil {
		panic(err)
	}

	// 控制台写入
	hostPort := inspectRes.NetworkSettings.Ports[containerPort][0]
	mongoURL = fmt.Sprintf("mongodb://%s:%s", hostPort.HostIP, hostPort.HostPort)

	return m.Run()
}

// NewClient 创建Mongo的连接
func NewClient(c context.Context) (*mongo.Client, error) {

	if mongoURL == "" {
		return nil, fmt.Errorf("mongo URL没有设置 RunWithMongoInDocker")
	}

	return mongo.Connect(c, options.Client().ApplyURI(mongoURL))
}

// NewDefaultClient 创建Mongo默认的连接
func NewDefaultClient(c context.Context) (*mongo.Client, error) {
	return mongo.Connect(c, options.Client().ApplyURI(defaultMongoURI))
}

// SetIndexes 创建索引
func SetIndexes(c context.Context, d *mongo.Database) error {

	_, err := d.Collection("account").Indexes().CreateOne(c, mongo.IndexModel{
		Keys: bson.D{
			{Key: "open_id", Value: 1},
		},
		Options: options.Index().SetUnique(true),
	})

	if err != nil {
		return err
	}

	_, err = d.Collection("trip").Indexes().CreateOne(c, mongo.IndexModel{
		Keys: bson.D{
			{Key: "trip.accountid", Value: 1},
			{Key: "trip.status", Value: 1},
		},
		Options: options.Index().SetUnique(true),
	})

	return err

}
